
package com.feib.soeasy.security;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import org.springframework.stereotype.Service;

import com.feib.soeasy.model.MenuItem;
import com.feib.soeasy.model.Role;
import com.feib.soeasy.service.MenuItemService;

/**
 * @title (#)SoeasyInvocationSecurityMetadataSourceService.java<br>
 * @description <br>
 * @author Anson Tsai<br>
 * @version 1.0.0 2010/11/25
 * @copyright Far Eastern International Bank Copyright (c) 2010<br>
 * @2010/11/30 create by Anson Tsai<br>
 */
@Service("soeasySecurityMetadataSource")
@Scope(value=BeanDefinition.SCOPE_SINGLETON)
public class SoeasyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

    private static final Logger logger = LoggerFactory.getLogger(SoeasyInvocationSecurityMetadataSourceService.class);
    
    @Resource(name = "menuItemService")
    private MenuItemService menuItemService;

    private UrlMatcher urlMatcher = new AntUrlPathMatcher();

    private Map<String, Collection<ConfigAttribute>> resourceMap = null;
    
    /**
     * 更新時間 
     * 2011.05.09 解決新增帳單業者 Menu 無法自動更新
     */
    private Date reloadTime;
    
    /**
     * 是否需要更新
     * 2011.05.09 解決新增帳單業者 Menu 無法自動更新
     */
    private boolean reload = false;

    @PostConstruct
    public void loadResourceDefine() {
        
        logger.trace("MyInvocationSecurityMetadataSourceService loadResourceDefine ");

        this.resourceMap = new HashMap<String, Collection<ConfigAttribute>>();

        // 通過數據庫中的信息設置，function 和 role
        for (MenuItem m : this.menuItemService.getAllMenuItemOrderMenuId()) {
            String action = m.getActionPage();
            logger.trace("Action：[" + action + "]");
            
            // 密碼變更，不列入控管
            if ("/Soez9011.action".equals(action))
                continue;

            if (StringUtils.isNotBlank(action)) {
                Collection<ConfigAttribute> value = null;
                if (resourceMap.containsKey(action)) {
                    value = resourceMap.get(action);
                }
                else {
                    value = new ArrayList<ConfigAttribute>();
                }

                for (Role r : m.getRoles()) {
                    logger.trace("Role：[" + r.getRolePk()+ "_" + r.getRoleName() + "]");
                    ConfigAttribute ca = new SecurityConfig(r.getRolePk()+ "_" + r.getRoleName());
                    if (!value.contains(ca))
                        value.add(ca);
                }
                resourceMap.put(action, value);// "xxx.jsp",
                                               // "ROLE_ADMIN,ROLE_USER"
            }
        }
        
        if (logger.isTraceEnabled())
        {
            logger.trace("----------action & role 關係  begin-----------------------------");
            for (String key : resourceMap.keySet()) {
                logger.trace("Action : " + key);
                Collection<ConfigAttribute> cs = resourceMap.get(key);
                for (ConfigAttribute c : cs) {
                    logger.trace("Role : " + c.getAttribute());
                }
            }
            logger.trace("----------action & role 關係  end-----------------------------");
        }
        
        reloadTime = new Date();
    }

    /*
     * (non-Javdoc)
     * 
     * @see org.springframework.security.access.SecurityMetadataSource#
     * getAllConfigAttributes()
     */
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        // TODO 自動產生方法 Stub
        return null;
    }

    /*
     * 取出屬於這個function的Collection<ConfigAttribute>(角色) /
     * 提供某個資源對應的權限定義，即getAttributes方法返回的結果。 (non-Javdoc)
     * 
     * @see
     * org.springframework.security.access.SecurityMetadataSource#getAttributes
     * (java.lang.Object)
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        
        if (logger.isTraceEnabled()) {
            logger.trace("getAttributes(Object) - start");
        }
        // guess object is a URL.
        String url = ((FilterInvocation) object).getRequestUrl();

        logger.trace("guess object URL : " + url);
        
        // 2011.05.09 解決新增帳單業者 Menu 無法自動更新
        if (reload)
        {
            // 設定 5 秒後 reload ，避免前一個transation 尚未commit時就抓取，會抓不到
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.HOUR, -4); // 搭配後面每四小時更新一次
            cal.add(Calendar.SECOND, -5);
            reloadTime = cal.getTime();
            reload = false;
        }
        else
        {
            Calendar cal = Calendar.getInstance();
            cal.setTime(reloadTime);
            cal.add(Calendar.HOUR, 4); //4 小時更新一次
            reloadTime = cal.getTime();
            
            if (new Date().after(reloadTime))
            {
                loadResourceDefine();
            }
        }

        Iterator<String> ite = resourceMap.keySet().iterator();
        while (ite.hasNext()) {
            String resURL = ite.next();
            if (urlMatcher.pathMatchesUrl(url, resURL)) {
                Collection<ConfigAttribute> returnCollection = resourceMap.get(resURL);
                if (logger.isTraceEnabled()) {
                    logger.trace("此URL有對應到DB中的Page(action)");
                    logger.trace("getAttributes(Object) - end");
                }
                return returnCollection;
            }
        }
        if (logger.isTraceEnabled()) {
            logger.trace("此URL沒有對應到DB中的Page(action)，即不存在於DB");
            logger.trace("getAttributes(Object) - end");
        }

        return null;
    }

    /*
     * (non-Javdoc)
     * 
     * @see
     * org.springframework.security.access.SecurityMetadataSource#supports(java
     * .lang.Class)
     */
    @Override
    public boolean supports(Class<?> arg0) {
        return true;
    }

    /**
     * @return 傳回 menuItemService。
     */
    public MenuItemService getMenuItemService() {
        return menuItemService;
    }

    /**
     * @param menuItemService 要設定的 menuItemService。
     */
    public void setMenuItemService(MenuItemService menuItemService) {
        this.menuItemService = menuItemService;
    }
    
    /*
     * 2011.05.09 解決新增帳單業者 Menu 無法自動更新
     */
    public void setToReload()
    {
        this.reload = true;
    }

}
